package com.thinkit.bigdata.web.service.impl.statistics;

import com.alibaba.fastjson.JSON;
import com.google.common.base.Strings;
import com.thinkit.bigdata.core.feature.orm.mybatis.Page;
import com.thinkit.bigdata.core.util.CastUtil;
import com.thinkit.bigdata.core.util.TaskUtil;
import com.thinkit.bigdata.core.util.es.ESUtil;
import com.thinkit.bigdata.core.util.thread.ThreadExecutorUtil;
import com.thinkit.bigdata.web.controller.pubmet.PubMethod;
import com.thinkit.bigdata.web.coremet.SystemStatic;
import com.thinkit.bigdata.web.dao.bus.BusModelRuleMapper;
import com.thinkit.bigdata.web.model.BaseEntity;
import com.thinkit.bigdata.web.model.bus.BusModelRule;
import com.thinkit.bigdata.web.model.es.audioEs.ProcessedInfos;
import com.thinkit.bigdata.web.model.es.audioEs.RuleDetails;
import com.thinkit.bigdata.web.model.es.audioEs.RuleInfos;
import com.thinkit.bigdata.web.model.es.audioEs.Source;
import com.thinkit.bigdata.web.model.result.HtmlResult;
import com.thinkit.bigdata.web.service.audioSearch.AudioSearchPubMet;
import com.thinkit.bigdata.web.service.statistics.RuleStaticsServer;
import com.thinkit.bigdata.web.thread.RuleStaticsThread;
import org.apache.log4j.Logger;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;

@Service
public class RuleStaticsServerImpl implements RuleStaticsServer {

    private static Logger logger = Logger.getLogger(RuleStaticsServerImpl.class);

    @Resource
    private ESUtil esUtil;

    @Resource
    private AudioSearchPubMet searchPubMet;

    @Resource
    private BusModelRuleMapper busModelRuleMapper;

    @Override
    public BoolQueryBuilder getQuery(String logTxt, HttpServletRequest request) {
        Map<String, String[]> params = new HashMap<>(request.getParameterMap());
        String provinceCode = request.getParameter("provinceCode");//省份
        if (!Strings.isNullOrEmpty(provinceCode)) {
            String customerlocs = request.getParameter("customerloc");//归属地
            params.put("customerloc", null);
            params.put("provinceCode", new String[]{JSON.toJSONString(searchPubMet.strFormatJson(provinceCode, customerlocs))});
        }
        return searchPubMet.getQuery(logTxt, params, request);
    }

    @Override
    public Map<String, List<Map<String, String>>> getRuleQuery(HttpServletRequest request, String type, String logTxt) throws Exception {
        Map<String, List<Map<String, String>>> resultM = new HashMap<>();
        List<Map<String, String>> resultList = new ArrayList<>();
        String provinceCode = request.getParameter("provinceCode");
        String workId = request.getParameter("workId");
        String groupId = request.getParameter("groupId");
        String seatId = request.getParameter("seatId");
        String seatName = request.getParameter("seatName");
        String callStartTime = request.getParameter("callStartTime");
        String callEndTime = request.getParameter("callEndTime");
        String ruleId = request.getParameter("ruleId");
        String ruleName = request.getParameter("ruleName");
        String useFor = request.getParameter("useFor");
        String fateful = request.getParameter("fateful");
        String signScore = request.getParameter("signScore");
        String satisfaction = request.getParameter("satisfaction");//考核
        String reCallFlag = request.getParameter("reCallFlag");//考核
        Integer pageNum = null;
        Integer pageSize = null;
        if ("search".equals(type)) {
            pageNum = Integer.parseInt(request.getParameter("pageNum"));//起始页
            pageSize = Integer.parseInt(request.getParameter("pageSize"));//每页条数
        }
        logger.info(logTxt + "provinceCode = " + provinceCode);
        logger.info(logTxt + "workId = " + workId);
        logger.info(logTxt + "groupId = " + groupId);
        logger.info(logTxt + "seatId = " + seatId);
        logger.info(logTxt + "seatName = " + seatName);
        logger.info(logTxt + "callStartTime = " + callStartTime);
        logger.info(logTxt + "callEndTime = " + callEndTime);
        logger.info(logTxt + "ruleId = " + ruleId);
        logger.info(logTxt + "ruleName = " + ruleName);
        logger.info(logTxt + "useFor = " + useFor);
        logger.info(logTxt + "fateful  = " + fateful);
        logger.info(logTxt + "signScore  = " + signScore);
        logger.info(logTxt + "satisfaction = " + satisfaction);
        logger.info(logTxt + "reCallFlag = " + reCallFlag);
        logger.info(logTxt + "pageNum = " + pageNum);
        logger.info(logTxt + "pageSize = " + pageSize);
        int count;
        List<BusModelRule> modelRuleList;
        Map<String, Object> map = new HashMap<>();
        if (!Strings.isNullOrEmpty(provinceCode)) {
            map.put("provinceCode", provinceCode);
        }
        map.put("workId", workId);
        if (!Strings.isNullOrEmpty(ruleId)) {
            map.put("ruleId", ruleId);
        }
        if (!Strings.isNullOrEmpty(fateful)) {
            map.put("fateful", fateful);
        }
        if (!Strings.isNullOrEmpty(signScore)) {
            map.put("signScore", signScore);
        }
        if (!Strings.isNullOrEmpty(useFor)) {
            map.put("useFor", useFor);
        }
        if (pageNum == null) {
            modelRuleList = busModelRuleMapper.selectByProCode(map);
            count = modelRuleList.size();
        } else {
            Page<BusModelRule> page = new Page<>(pageNum + 1, pageSize);
            modelRuleList = busModelRuleMapper.selectByProCode(page, map);
            count = page.getTotalCount();
        }
        logger.info(logTxt + "---------------start-------------------- = " + System.currentTimeMillis());
        for (BusModelRule modelRule : modelRuleList) {
            BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
            //数据权限
            esUtil.formatQ(queryBuilder, null);
            if (!Strings.isNullOrEmpty(callStartTime)) {
                PubMethod.formatTime(queryBuilder, callStartTime, callEndTime);
            }
            if (!Strings.isNullOrEmpty(provinceCode)) {
                queryBuilder.must(QueryBuilders.matchQuery("voiceInfo.provinceCode", provinceCode));
            }
            if (!Strings.isNullOrEmpty(groupId)) {
                queryBuilder.must(QueryBuilders.matchQuery("voiceInfo.groupId", groupId));
            }
            if (!Strings.isNullOrEmpty(seatId)) {
                queryBuilder.must(QueryBuilders.matchQuery("voiceInfo.seatId", seatId));
            }
            if (!Strings.isNullOrEmpty(seatName)) {
                queryBuilder.must(QueryBuilders.matchQuery("voiceInfo.seatName", seatName));
            }
            if (!Strings.isNullOrEmpty(satisfaction)) {
                queryBuilder.must(QueryBuilders.matchQuery("voiceInfo.satisfaction", satisfaction));
            }
            if (!Strings.isNullOrEmpty(reCallFlag)) {
                queryBuilder.must(QueryBuilders.matchQuery("voiceInfo.reCallFlag", reCallFlag));
            }
            BoolQueryBuilder query = new BoolQueryBuilder();
            query.must(QueryBuilders.matchQuery("workScores.modelRules.ruleId", modelRule.getRuleid()));
            queryBuilder.must(
                    QueryBuilders.nestedQuery(
                            "workScores",
                            QueryBuilders.boolQuery()
                                    .must(QueryBuilders.matchQuery("workScores.workId", workId))
                                    .must(
                                            QueryBuilders.nestedQuery(
                                                    "workScores.modelRules",
                                                    QueryBuilders.matchQuery(
                                                            "workScores.modelRules.ruleId",
                                                            modelRule.getRuleid()
                                                    ),
                                                    ScoreMode.None
                                            )
                                    ),
                            ScoreMode.None)
            );
            logger.info(logTxt + "ruleId = " + modelRule.getRuleid());
            //todo多线程 查询es 转换结果
            List<Source> sources = new ArrayList<>();
            int esCount = SystemStatic.crud.queryQBuilderAllByMes(SystemStatic.AUDIO_ES_INDEX, SystemStatic.AUDIO_ES_TYPE, queryBuilder);
            logger.info(logTxt + "ES总数：esCount = " + esCount);
            int esPageSize = 50;
            List<Integer> pageTount = TaskUtil.formatSum(esCount, esPageSize);//todo最后一个数字需要特殊处理:6022
            int semaphoreNum = pageTount.size() > 30 ? 30 : pageTount.size();
            logger.info(logTxt + "Semaphore机器数 = " + semaphoreNum);
            Semaphore semaphore = new Semaphore(semaphoreNum);//机器数
            CountDownLatch countDownLatch = new CountDownLatch(pageTount.size());
            for (Integer aPageTount : pageTount) {
                ThreadExecutorUtil.executeTask(new RuleStaticsThread(countDownLatch, semaphore, esUtil, aPageTount, esPageSize, queryBuilder, sources));
            }
            countDownLatch.await();
            logger.info(logTxt + "sources.size = " + sources.size());
            Map<String, String> resultMap = new HashMap<>();
            resultMap.put("provinceCode", provinceCode);
            resultMap.put("workId", workId);
            resultMap.put("groupId", groupId);
            resultMap.put("seatId", seatId);
            resultMap.put("callStartTime", callStartTime);
            resultMap.put("callEndTime", callEndTime);
            resultMap.put("ruleId", modelRule.getRuleid().toString());
            resultMap.put("ruleName", modelRule.getRulename());
            resultMap.put("ruleType", modelRule.getRuletypename());
            Map<String, Integer> countMap = hitCount(sources, modelRule.getRuleid().toString());
            //总命中量
            Integer hitCount = countMap.get("hitCount");
            //系统命中量
            Integer sysHitCount = countMap.get("sysHitCount");
            //人工添加量(漏识量)
            Integer leakageCount = countMap.get("leakageCount");
            //人工纠正量(正确量)
            Integer accuracyCount = countMap.get("accuracyCount");
            //申诉量
            Integer appealCount = countMap.get("appealCount");
            //驳回量
            Integer rejectCount = countMap.get("rejectCount");
            //申诉成功
            Integer appealOkCount = countMap.get("appealOkCount");
            //规则属性
            resultMap.put("useFor", modelRule.getUserfor() + "");
            //致命性
            resultMap.put("fateful", modelRule.getFateful() + "");
            if (modelRule.getSignscore() == BaseEntity.SIGNSCORE_ADD) {//加分项规则
                //致命性
                resultMap.put("fateful", BaseEntity.FATEFUL_ADD + "");
            }
            //是否加分项
            resultMap.put("signScore", modelRule.getSignscore() + "");
            //总命中量
            resultMap.put("hitCount", hitCount + "");
            if (modelRule.getUserfor() == BaseEntity.USEFOR_SYS) {//系统
                //系统命中量
                resultMap.put("sysHitCount", sysHitCount + "");
                //人工添加量(漏识量)
                resultMap.put("leakageCount", leakageCount + "");
                //人工纠正量(正确量)
                resultMap.put("accuracyCount", accuracyCount + "");
                //总命中量
                resultMap.put("hitCount", (sysHitCount + leakageCount - accuracyCount) + "");
                //系统正确率
                if (hitCount != 0) {
                    resultMap.put("accuracy", CastUtil.formatDec((sysHitCount - accuracyCount), (sysHitCount + leakageCount)));
                } else {
                    resultMap.put("accuracy", "0.00%");
                }
                //系统漏识率
                if (hitCount != 0) {
                    resultMap.put("leakage", CastUtil.formatDec(leakageCount, hitCount));
                } else {
                    resultMap.put("leakage", "0.00%");
                }
            } else {
                //系统命中量
                resultMap.put("sysHitCount", "--");
                //人工添加量(漏识量)
                resultMap.put("leakageCount", "--");
                //人工纠正量(正确量)
                resultMap.put("accuracyCount", "--");
                //系统正确率
                resultMap.put("accuracy", "--");
                //系统漏识率
                resultMap.put("leakage", "--");
            }
            //申诉量
            resultMap.put("appealCount", appealCount + "");
            //驳回量
            resultMap.put("rejectCount", rejectCount + "");
            //申诉成功
            resultMap.put("appealOkCount", appealOkCount + "");
            //申诉率
            if (hitCount != 0) {
                resultMap.put("appeal", CastUtil.formatDec(appealCount, hitCount));
            } else {
                resultMap.put("appeal", "0.00%");
            }
            //申诉成功率
            if (hitCount != 0) {
                resultMap.put("appealOk", CastUtil.formatDec(appealOkCount, appealCount));
            } else {
                resultMap.put("appealOk", "0.00%");
            }
            resultList.add(resultMap);
        }
        logger.info(logTxt + "---------------end-------------------- = " + System.currentTimeMillis());
        List<Map<String, String>> countList = new ArrayList<>();
        Map<String, String> countMap = new HashMap<>();
        countMap.put("count", Integer.toString(count));
        countList.add(countMap);
        resultM.put("countList", countList);
        resultM.put("resultList", resultList);
        return resultM;
    }

    //命中量 申诉量 驳回量 申诉成功
    private static Map<String, Integer>  hitCount(List<Source> sources, String ruleId) {
        Map<String, Integer> countMap = new HashMap<>();
        //命中量
        Integer hitCount = 0;
        //系统命中量
        Integer sysHitCount = 0;
        //人工添加量(漏识量)
        Integer leakageCount = 0;
        //人工纠正量(正确量)
        Integer accuracyCount = 0;
        //申诉量
        Integer appealCount = 0;
        //驳回量
        Integer rejectCount = 0;
        //申诉成功
        Integer appealOkCount = 0;
        for (Source source : sources) {
            if (source == null) {
                continue;
            }
            if (source.getRuleInfos() == null || source.getRuleInfos().isEmpty()) {
                continue;
            }
            for (RuleInfos rule : source.getRuleInfos()) {
                if (ruleId.equals(rule.getRuleId().toString())) {
                    for (RuleDetails rd : rule.getRuleDetails()) {
                        if (rd.getMatchStatus() == BaseEntity.MATCHSTATUS_NO
                                && rd.getProStatus() == BaseEntity.SM_WORK_PROSTATUS_APPEAL_BEGIN) {//纠错 但不是申诉操作
                            accuracyCount++;
                        }
                        if (rd.getIsMissing() == BaseEntity.ISMISSING_YES) {//漏识
                            leakageCount++;
                        }
                        if (rd.getProStatus() != BaseEntity.SM_WORK_PROSTATUS_APPEAL_BEGIN) {//已申诉
                            appealCount++;
                            boolean appealYesOrNo = false;
                            for (ProcessedInfos pds : rd.getProcessedInfos()) {
                                if (BaseEntity.SM_WORK_PROSTATUS_APPEAL_YES == pds.getProStatus()) {
                                    appealYesOrNo = true;
                                }
                            }
                            if (appealYesOrNo) {//2申诉成功
                                appealOkCount++;
                            } else {//3申诉驳回
                                rejectCount++;
                            }
                        }
                    }
                    hitCount += rule.getRuleTimes();
                    for (RuleDetails rd : rule.getRuleDetails()) {
                        if (rd.getIsMissing() == BaseEntity.ISMISSING_NO) {//不是漏识
                            sysHitCount++;
                        }
                    }
                }
            }
        }
        countMap.put("hitCount", hitCount);
        countMap.put("sysHitCount", sysHitCount);
        countMap.put("appealCount", appealCount);
        countMap.put("appealOkCount", appealOkCount);
        countMap.put("rejectCount", rejectCount);
        countMap.put("accuracyCount", accuracyCount);
        countMap.put("leakageCount", leakageCount);
        return countMap;
    }

    @Override
    public HtmlResult getRuleCount(HttpServletRequest request, String logTxt) throws Exception {
        Map<String, List<Map<String, String>>> result = getRuleQuery(request, "search", logTxt);
        List<Map<String, String>> resultList = result.get("resultList");
        return HtmlResult.success(resultList, Integer.parseInt(result.get("countList").get(0).get("count")));
    }
}
